package com.sjht.android.bxgz.manager;

import java.io.BufferedOutputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.ref.SoftReference;
import java.lang.ref.WeakReference;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.os.AsyncTask;
import android.os.Handler;
import android.widget.ImageView;

import com.sjht.android.bxgz.BxgzConstants;
import com.sjht.android.bxgz.network.AsyncHttpClient;

public class ImageDownloader {

    private int hard_cache_capcity = 40;
    private int delay_before_purge = 30 * 1000; // in milliseconds
    
    private static final int SOFT_CACHE_CAPACITY = 40;
    private static final int TEMP_CACHE_CAPACITY = 20;

	private final HashMap<String, Bitmap> sHardBitmapCache =
        new LinkedHashMap<String, Bitmap>(hard_cache_capcity / 2, 0.75f, true) {
        private static final long serialVersionUID = -7190622541619388252L;
        @Override
        protected boolean removeEldestEntry(LinkedHashMap.Entry<String, Bitmap> eldest) {
            if (size() > hard_cache_capcity) {
//            	checkQueue();
                sSoftBitmapCache.put(eldest.getKey(), new SoftReference<Bitmap>(eldest.getValue()));
//            	sTempBitmapCache.put(eldest.getKey(), eldest.getValue());
                return true;
            } else {
                return false;
            }
        }
    };

    private static final HashMap<String, Bitmap> sTempBitmapCache =
        new LinkedHashMap<String, Bitmap>(TEMP_CACHE_CAPACITY / 2, 0.75f, true) {
        private static final long serialVersionUID = -7190622541619388252L;
        @Override
        protected boolean removeEldestEntry(LinkedHashMap.Entry<String, Bitmap> eldest) {
            if (size() > SOFT_CACHE_CAPACITY) {
            	recycleBitmap(eldest.getValue());
                return true;
            } else {
                return false;
            }
        }
    };
    
    private final static ConcurrentHashMap<String, SoftReference<Bitmap>> sSoftBitmapCache =
        new ConcurrentHashMap<String, SoftReference<Bitmap>>(SOFT_CACHE_CAPACITY / 2);
    
//    private final static ReferenceQueue<Bitmap> queue = new ReferenceQueue<Bitmap>();
    
//    private final class BitmapSoftReference extends SoftReference<Bitmap> {
//		public BitmapSoftReference(Bitmap r, ReferenceQueue<? super Bitmap> q) {
//			super(r, q);
//		}
//    }
    
    private final Handler purgeHandler = new Handler();

    private final Runnable purger = new Runnable() {
        public void run() {
            clearCache();
        }
    };

//    private void checkQueue() {
//    	BitmapSoftReference reference = null;
//		while ((reference = (BitmapSoftReference) queue.poll()) != null) {
//			Log.i(LOG_TAG, "checkQueue, bitmap == null" + (reference.get() == null));
//			reference.get().recycle();
//			recycleBitmap(reference.get());
//		}
//    }
    
    private static void recycleBitmap(Bitmap bitmap) {
    	if(bitmap != null && !bitmap.isRecycled()) {
    		bitmap.recycle();
    		bitmap = null;
    	}
    }
    
    public static void clearTempCache() {
    	if(sTempBitmapCache != null && sTempBitmapCache.size() > 0) {
    		Set<Entry<String, Bitmap>> entrySet = sTempBitmapCache.entrySet();
    		for (Entry<String, Bitmap> entry : entrySet) {
				recycleBitmap(entry.getValue());
			}
    		sTempBitmapCache.clear();
    	}
    	
    }
    
    public void download(String url, ImageView imageView) {
        download(url, imageView, null, null);
    }
    
    public void download(String url, ImageView imageView, Bitmap drawable) {
        download(url, imageView, null, drawable);
    }

    public void download(String url, ImageView imageView, String cookie, Bitmap drawable) {
        resetPurgeTimer();
        Bitmap bitmap = getBitmapFromCache(url);
        if (bitmap == null) {
            forceDownload(url, imageView, cookie, drawable);
        } else {
            cancelPotentialDownload(url, imageView);
            imageView.setImageBitmap(bitmap);
        }
    }

    private void forceDownload(String url, ImageView imageView, String cookie, Bitmap drawable) {
        if (url == null) {
            imageView.setImageDrawable(null);
            return;
        }

        if (cancelPotentialDownload(url, imageView)) {
            BitmapDownloaderTask task = new BitmapDownloaderTask(imageView);
            DownloadedBitmap downloadedDrawable = new DownloadedBitmap(task, drawable);
        	imageView.setImageDrawable(downloadedDrawable);
            task.execute(url, cookie);
        }
    }

    public void clearCache() {
        sHardBitmapCache.clear();
        sSoftBitmapCache.clear();
    }

    private void resetPurgeTimer() {
        purgeHandler.removeCallbacks(purger);
        purgeHandler.postDelayed(purger, delay_before_purge);
    }

    private static boolean cancelPotentialDownload(String url, ImageView imageView) {
        BitmapDownloaderTask bitmapDownloaderTask = getBitmapDownloaderTask(imageView);

        if (bitmapDownloaderTask != null) {
            String bitmapUrl = bitmapDownloaderTask.url;
            if ((bitmapUrl == null) || (!bitmapUrl.equals(url))) {
                bitmapDownloaderTask.cancel(true);
            } else {
                return false;
            }
        }
        return true;
    }

    private static BitmapDownloaderTask getBitmapDownloaderTask(ImageView imageView) {
        if (imageView != null) {
            Drawable drawable = imageView.getDrawable();
            if (drawable instanceof DownloadedBitmap) {
            	DownloadedBitmap downloadedDrawable = (DownloadedBitmap) drawable;
                return downloadedDrawable.getBitmapDownloaderTask();
            }
        }
        return null;
    }

    private Bitmap getBitmapFromCache(String url) {
        synchronized (sHardBitmapCache) {
            final Bitmap bitmap = sHardBitmapCache.get(url);
            if (bitmap != null) {
                sHardBitmapCache.remove(url);
                sHardBitmapCache.put(url, bitmap);
                return bitmap;
            }
        }

        SoftReference<Bitmap> bitmapReference = sSoftBitmapCache.get(url);
        if (bitmapReference != null) {
            final Bitmap bitmap = bitmapReference.get();
            if (bitmap != null) {
                return bitmap;
            } else {
                sSoftBitmapCache.remove(url);
            }
        }
        
        if (FileManager.checkSDCardRead()) {
			String path = BxgzConstants.PIC + url.hashCode();
			if (FileManager.checkSDCardFile(path)) {
					final Bitmap bitmap = BitmapFactory.decodeFile(path);
					if (bitmap != null) {
						synchronized (sHardBitmapCache) {
		                    sHardBitmapCache.put(url, bitmap);
		                }
						return bitmap;
					}
//				}
			}
		} 
        
        return null;
    }

    class BitmapDownloaderTask extends AsyncTask<String, Void, Bitmap> {
        private static final int IO_BUFFER_SIZE = 4 * 1024;
        private String url;
        private final WeakReference<ImageView> imageViewReference;

        public BitmapDownloaderTask(ImageView imageView) {
            imageViewReference = new WeakReference<ImageView>(imageView);
        }

        @Override
        protected Bitmap doInBackground(String... params) {
        	url = params[0];
        	
        	
        	
            try {
            	AsyncHttpClient client = new AsyncHttpClient();
            	InputStream inputStream = client.execute(url);
                if (inputStream != null) {
                    OutputStream outputStream = null;
                    FileInputStream fis = null;
                    try {
                        final ByteArrayOutputStream dataStream = new ByteArrayOutputStream();
                        outputStream = new BufferedOutputStream(dataStream, IO_BUFFER_SIZE);
                        copy(inputStream, outputStream);
                        outputStream.flush();

                        final byte[] data = dataStream.toByteArray();
                        
                        if (FileManager.checkSDCardWriter() && FileManager.checkSDCardCapacity(500)) {
        					File file = new File(BxgzConstants.PIC + url.hashCode());
        					if (file != null && file.length() <= 0) {
    							if (!file.getParentFile().exists()) {
        							file.getParentFile().mkdirs();
        						}
    							FileOutputStream fos = new FileOutputStream(file);
        						fos.write(data, 0, data.length);
        						fos.flush();
        						fos.close();
        					}
        					fis = new FileInputStream(file);
        				}
						final Bitmap bitmap = BitmapFactory.decodeByteArray(data, 0, data.length);
						if (bitmap != null) {
							return bitmap;
						}

                    } finally {
                    	if (fis != null) {
                    		fis.close();
                    	}
                        if (inputStream != null) {
                            inputStream.close();
                        }
                        if (outputStream != null) {
                            outputStream.close();
                        }
                    }
                }
            } catch (IOException e) {
            	e.printStackTrace();
            } catch (IllegalStateException e) {
            	e.printStackTrace();
            } catch (Exception e) {
            	e.printStackTrace();
            } finally {
//                if (client != null) {
//                    client.close();
//                }
            }
            return null;
        }

        @Override
        protected void onPostExecute(Bitmap bitmap) {
            if (isCancelled()) {
                bitmap = null;
            }

            if (bitmap != null) {
                synchronized (sHardBitmapCache) {
                    sHardBitmapCache.put(url, bitmap);
                }
                
                if (imageViewReference != null) {
                    ImageView imageView = imageViewReference.get();
                    BitmapDownloaderTask bitmapDownloaderTask = getBitmapDownloaderTask(imageView);
                    if (this == bitmapDownloaderTask) {
                        imageView.setImageBitmap(bitmap);
                    }
                }
            }
        }

        public void copy(InputStream in, OutputStream out) throws IOException {
            byte[] b = new byte[IO_BUFFER_SIZE];
            int read;
            while ((read = in.read(b)) != -1) {
                out.write(b, 0, read);
            }
        }
    }

    static class DownloadedDrawable extends ColorDrawable {
        private final WeakReference<BitmapDownloaderTask> bitmapDownloaderTaskReference;
        final Drawable drawable;

        public DownloadedDrawable(BitmapDownloaderTask bitmapDownloaderTask, Drawable drawable) {
        	this.drawable = drawable;
        	bitmapDownloaderTaskReference =
                new WeakReference<BitmapDownloaderTask>(bitmapDownloaderTask);
        }

        public BitmapDownloaderTask getBitmapDownloaderTask() {
            return bitmapDownloaderTaskReference.get();
        }
    }
    
	public static class DownloadedBitmap extends BitmapDrawable {
		private final WeakReference<BitmapDownloaderTask> bitmapDownloaderTaskReference;

		public DownloadedBitmap(BitmapDownloaderTask bitmapDownloaderTask, Bitmap bitmap) {
			super(bitmap);
			bitmapDownloaderTaskReference = new WeakReference<BitmapDownloaderTask>(bitmapDownloaderTask);
		}

		public BitmapDownloaderTask getBitmapDownloaderTask() {
			return bitmapDownloaderTaskReference.get();
		}
	}
    
	public ImageDownloader(int hard_cache_capcity, int delay_before_purge) {
		this.hard_cache_capcity = hard_cache_capcity;
		this.delay_before_purge = delay_before_purge;
	}



	public static int computeSampleSize(BitmapFactory.Options options,
	        int minSideLength, int maxNumOfPixels) {
	    int initialSize = computeInitialSampleSize(options, minSideLength,
	            maxNumOfPixels);
	    int roundedSize;
	    if (initialSize <= 8) {
	        roundedSize = 1;
	        while (roundedSize < initialSize) {
	            roundedSize <<= 1;
	        }
	    } else {
	        roundedSize = (initialSize + 7) / 8 * 8;
	    }
	    return roundedSize;
	}
	
	private static int computeInitialSampleSize(BitmapFactory.Options options,
        int minSideLength, int maxNumOfPixels) {
	    double w = options.outWidth;
	    double h = options.outHeight;
	    int lowerBound = (maxNumOfPixels == -1) ? 1 :
	            (int) Math.ceil(Math.sqrt(w * h / maxNumOfPixels));
	    int upperBound = (minSideLength == -1) ? 128 :
	            (int) Math.min(Math.floor(w / minSideLength),
	            Math.floor(h / minSideLength));
	    if (upperBound < lowerBound) {
	        return lowerBound;
	    }
	    if ((maxNumOfPixels == -1) &&
	            (minSideLength == -1)) {
	        return 1;
	    } else if (minSideLength == -1) {
	        return lowerBound;
	    } else {
	        return upperBound;
	    }
	}   

	
	public ImageDownloader() {}
}